#pragma once 
#include<iostream>
#include"Socket.hpp"

static const uint16_t default_port=8080;
static const int max_num_array=sizeof(fd_set)*8;//以字节为单位
static const int default_data=-1;//fd_array数组的默认初始值

class SelectServer
{
public:
    SelectServer(uint16_t port=default_port):_port(port)
    {
        for(int i=1;i<max_num_array;i++)
            fd_array[i]=default_data;//初始化数组值
    }
    bool Init()
    {
        _listensock.Socket();//创建套接字
        _listensock.Bind(_port);//绑定套接字
        _listensock.Listen();//将套接字设为监听状态
    }
    void Hander_Link(fd_set& rfds)
    {
        for(int i=0;i<max_num_array;i++)
        {
            if(FD_ISSET(fd_array[i],&rfds))//遍历一遍fd_array数组，判断哪些fd是新建立的链接
            {
                //判断fd_array[i]是“监听”套接字，还是read阻塞响应套接字
                if(fd_array[i]==_listensock.sockfd())//是“监听”套接字，就先accpte从内核拿到上层，再仍回select
                {
                    string client_ip;
                    uint16_t client_port;
                    int sock=_listensock.Accept(client_ip,client_port);//将新建立的链接拿到上层
                    cout<<"accept socket successfully,sock:"<<sock<<",errno:"<<errno<<",strerror:"<<strerror(errno)<<endl;

                    //ssize_t n=read(sock,buffer,sizeof(buffer));//不能直接read，因为若对方没向服务端发消息的话，会阻塞等待
                    int pos=0;
                    for(int pos=0;pos<max_num_array;pos++)
                        if(fd_array[pos]==default_data) {
                            fd_array[pos]=sock;//所以，我们需要把sock再次交给select，让select帮我们判断sock的接收缓冲区是否有消息
                            break;
                        }
                    if(pos==max_num_array)//fd_array数组满了，没法继续处理，只好把sock关了
                    {
                        cerr<<"server is full,close sock:"<<sock<<endl;
                        close(sock);
                    }
                }
                else//是read响应套接字，就调read直接从文件缓冲区把数据读出来
                {
                    char buffer[1024];
                    ssize_t n=read(fd_array[i],buffer,sizeof(buffer)-1);//bug  tcp面向字节流，读取的报文可能不完整——>Encode、Decode + 序列化、反序列化
                    if(n>0)
                    {
                        buffer[n-1]='\0';//buffer中含回车
                        cout<<"receive a new data:"<<buffer<<endl;
                    }
                    else if(n==0)
                    {
                        cout<<"counterpart have closed sock, so i will close it..."<<endl;
                        close(fd_array[i]);
                        fd_array[i]=default_data;//链接都已经关了，select就没必要再继续关心这个fd了
                    }
                    else 
                    {
                        cerr<<"read failed，so i will close it..."<<endl;
                        close(fd_array[i]);
                        fd_array[i]=default_data;//读取出错，select也没必要再继续关心这个fd了
                    }
                }
            }
        }
    }
    void Start()
    {
        int listensock=_listensock.sockfd();
        fd_array[0]=listensock;
        
        while(true)
        {
            //不能用accept直接接收，而应该用select取代accept的功能

            fd_set rfds;//本质是一个位图，大小是128字节，即1024个比特位
            struct timeval timeout={1,0};//每隔5.0s让select返回一次结果，且timeout是输入输出型参数，需周期性重置
            int maxfd=fd_array[0];

            FD_ZERO(&rfds);//清空位图

            for(int i=0;i<max_num_array;i++)
                if(fd_array[i]!=default_data)
                    FD_SET(fd_array[i],&rfds),maxfd=fd_array[i];//将listensock设置到rfds中，即告诉select：关心listensock的等待信息，且rfds是输入输出型参数，需周期性重置

            cout<<"maxfd update to:"<<maxfd<<endl;

            int n=select(maxfd+1,&rfds,nullptr,nullptr,/*&timeout*/nullptr);//阻塞式等待
            switch(n)
            {
                case 0:
                    cout<<"等待超时..."<<timeout.tv_sec<<"."<<timeout.tv_usec<<endl;
                    break;
                case -1:
                    cerr<<"等待结果出错..."<<"errno:"<<errno<<",strerror:"<<strerror(errno)<<endl;
                    break;
                default:
                    cout<<"建立了一个新的链接..."<<endl;
                    Hander_Link(rfds);//select已经承担了“等待新链接”的工作，再次accept时，可将fd直接拿到上层，无需等待
                    break;
            }
        }
    }

    ~SelectServer()
    {
        _listensock.Close();
    }

private:
    Sock _listensock;
    uint16_t _port;
    int fd_array[max_num_array];//存放所有“select”需要关心的fd值
};
//select的缺陷
//1.等待的fd是有上限的
//2.输入输出参数是由"同一个"变量控制的，每次输出的结果都会改变rfds的值，必须重新对rfds重置，遍历麻烦
//    
// 引出——> poll(struct pollfd* fds, nfds_t nfds, int timeout);   